#include <my_head.h>

typedef struct _Msg{
    char type;      // 操作码  'L' 登录  'C' 群聊  'Q' 退出
    char name[32];  // 用户名
    char data[256]; // 消息
}msg_t;

typedef struct _Node{
    struct sockaddr_in clientaddr;
    struct _Node *next;
}node_t;

// 登录操作的函数
void do_login(msg_t msg, int sockfd, struct sockaddr_in clientaddr, node_t *phead){
    // 遍历链表把 xxx加入群聊 的消息发给在线的所有人
    node_t *ptemp = phead;
    while(NULL != ptemp->next){
        ptemp = ptemp->next;
        if(-1 == sendto(sockfd, &msg, sizeof(msg), 0, \
                (struct sockaddr *)&(ptemp->clientaddr), sizeof(ptemp->clientaddr))){
            ERR_LOG("sendto error");
        }
    }
    // 把新加入群聊的客户端的网络信息结构体添加到链表中
    node_t *pnew = (node_t *)malloc(sizeof(node_t));
    if(NULL == pnew){
        printf("内存分配失败\n");
        exit(EXIT_FAILURE);
    }
    pnew->clientaddr = clientaddr;
    pnew->next = phead->next;
    phead->next = pnew;
    return;
}

// 群聊操作的函数
void do_chat(msg_t msg, int sockfd, struct sockaddr_in clientaddr, node_t *phead){
    // 遍历链表把 群聊 的消息发给除了自己之外的所有人
    node_t *ptemp = phead;
    while(NULL != ptemp->next){
        ptemp = ptemp->next;
        if(memcmp(&clientaddr, &(ptemp->clientaddr), sizeof(clientaddr))){
            if(-1 == sendto(sockfd, &msg, sizeof(msg), 0, \
                (struct sockaddr *)&(ptemp->clientaddr), sizeof(ptemp->clientaddr))){
                ERR_LOG("sendto error");
            }
        }
    }
    return;
}

// 退出操作的函数
void do_quit(msg_t msg, int sockfd, struct sockaddr_in clientaddr, node_t *phead){
    // 把 退出 的客户端在链表中删除
    // 遍历链表把 xxx退出群聊 的消息发给所有人
    node_t *ptemp = phead;
    while(NULL != ptemp->next){
        if(memcmp(&clientaddr, &(ptemp->next->clientaddr), sizeof(clientaddr))){
            // 不是自己 就发数据
            if(-1 == sendto(sockfd, &msg, sizeof(msg), 0, \
                (struct sockaddr *)&(ptemp->next->clientaddr), sizeof(ptemp->next->clientaddr))){
                ERR_LOG("sendto error");
            }
            ptemp = ptemp->next;
        }else{
            // 是自己 就把自己在链表中删除
            node_t *pdel = ptemp->next;
            ptemp->next = pdel->next;
            free(pdel);
            pdel = NULL;
        }
    }
    return;
}

int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(-1 == sockfd)
        ERR_LOG("socket error");
    
    // 填充服务器网络信息结构体
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t serveraddr_len = sizeof(serveraddr);

    // 将套接字和服务器网络信息结构体绑定
    if(-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
        ERR_LOG("bind error");

    // 定义结构体 保存客户端的信息
    struct sockaddr_in clientaddr;
    socklen_t clientaddr_len = sizeof(clientaddr);

    msg_t msg;
    // 创建父子进程
    pid_t pid = 0;
    if(-1 == (pid = fork())){
        ERR_LOG("fork error");
    }else if(0 == pid){
        // 创建链表头结点
        node_t *phead = (node_t *)malloc(sizeof(node_t));
        if(NULL == phead){
            printf("内存分配失败\n");
            exit(EXIT_FAILURE);
        }
        memset(phead, 0, sizeof(node_t)); // 初始化头结点
        // 子进程 负责 接收客户端发来的数据 并处理
        while(1){
            if(-1 == recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&clientaddr, &clientaddr_len)){
                ERR_LOG("recvfrom error");
            }
            printf("%-10s : %s\n", msg.name, msg.data); // debug
            switch(msg.type){
                case 'L':
                    do_login(msg, sockfd, clientaddr, phead);
                    break;
                case 'C':
                    do_chat(msg, sockfd, clientaddr, phead);
                    break;
                case 'Q':
                    do_quit(msg, sockfd, clientaddr, phead);
                    break;
            }
        }
    }else if(0 < pid){
        // 父进程 负责 发送系统消息
        // 把父进程当做一个客户端 以 群聊 的方式把系统消息发给子进程
        // 由子进程转发给在线的所有人
        msg.type = 'C';
        strcpy(msg.name, "server");
        while(1){
            fgets(msg.data, sizeof(msg.data), stdin);
            msg.data[strlen(msg.data)-1] = '\0';
            if(-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&serveraddr, serveraddr_len)){
                ERR_LOG("sendto error");
            }
        }
    }
    // 关闭套接字
    close(sockfd);

    return 0;
}